home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / misc / sci / ephem_src_4_28.lha / io.c.1 < prev    next >
Text File  |  1992-05-24  |  21KB  |  856 lines

  1. /* this file (in principle) contains all the device-dependent code for
  2.  * handling screen movement and reading the keyboard. public routines are:
  3.  *   c_pos(r,c), c_erase(), c_eol();
  4.  *   chk_char(), read_char(), read_line (buf, max); and
  5.  *   byetty().
  6.  * N.B. we assume output may be performed by printf(), putchar() and
  7.  *   fputs(stdout). since these are buffered we flush first in read_char().
  8.  */
  9.  
  10. /* explanation of various conditional #define options:
  11.  * UNIX: uses termcap for screen management.
  12.  *   USE_NDELAY: does non-blocking tty reads with fcntl(O_NDELAY); otherwise
  13.  *     this is done with ioctl(..,FIONREAD..). Use which ever works on your
  14.  *     system.
  15.  *   USE_TERMIO: use termio.h instead of older generic sgtty.h.
  16.  * TURBO_C: compiles for Turbo C 2.0. I'm told it works for Lattice and
  17.  *     Microsoft too.
  18.  *   USE_ANSISYS: default PC cursor control uses direct BIOS calls (thanks to
  19.  *     Mr. Doug McDonald). If your PC does not work with this, however, add
  20.  *     "device ANSI.SYS" to your config.sys file and build ephem with
  21.  *     USE_ANSISYS.
  22.  * VMS: uses QIO for input, TERMTABLE info for output. This code uses only
  23.  *     standard VMS calls, i.e. it does not rely on any third-vendor termcap
  24.  *     package or the like. The code includes recoqnition of arrow keys, it
  25.  *     is easy to extend it to recoqnize other function keys. also, you don't
  26.  *     really need to #define VMS since it is inherent in the compiler.
  27.  *     (thanks to Mr. Karsten Spang, NBI, Copenhagen, spang@nbivax.nbi.dk)
  28.  */
  29.  
  30. /* define one of these... */
  31. /*#define UNIX */
  32. /* #define VMS */
  33. /* #define TURBO_C */
  34.  
  35. #ifdef AMIGA
  36. #include <exec/types.h>
  37. #include <libraries/dos.h>
  38. #ifdef __SASC
  39. #include <proto/dos.h>
  40. #include <ios1.h>
  41. #include <time.h>
  42. #include <stdlib.h>
  43. extern struct UFB _ufbs[];
  44. extern char *_TZ;
  45. #endif
  46. static int ttysetup = 0;
  47. static BPTR winfh;
  48. #define TURBO_C        /* kludge... */
  49. #define USE_ANSISYS
  50. #endif
  51.  
  52. /* then if you defined UNIX you want this too if you don't have FIONREAD */
  53. /* #define USE_NDELAY */
  54.  
  55. /* and then if you defined UNIX you want this too if using sgtty.h won't compile
  56.  * for you.
  57.  */
  58. /* #define USE_TERMIO */
  59.  
  60. /* if you defined TURBO_C you might want this too if screen io looks garbled */
  61. /* #define USE_ANSISYS */
  62.  
  63. #include <stdio.h>
  64. #include "screen.h"
  65.  
  66. #ifdef UNIX
  67. #include <signal.h>
  68. #ifdef USE_TERMIO
  69. #include <termio.h>
  70. #else
  71. #include <sgtty.h>
  72. #endif
  73. #ifdef USE_NDELAY
  74. #include <fcntl.h>
  75. #endif
  76.  
  77. extern char *tgoto();
  78. static char *cm, *ce, *cl, *kl, *kr, *ku, *kd; /* curses sequences */
  79. static int tloaded;
  80. static int ttysetup;
  81. #ifdef USE_TERMIO
  82. static struct termio orig_tio;
  83. #else
  84. static struct sgttyb orig_sgtty;
  85. #endif
  86.  
  87. /* move cursor to row, col, 1-based.
  88.  * we assume this also moves a visible cursor to this location.
  89.  */
  90. c_pos (r, c)
  91. int r, c;
  92. {
  93.     if (!tloaded) tload();
  94.     fputs (tgoto (cm, c-1, r-1), stdout);
  95. }
  96.  
  97. /* erase entire screen. */
  98. c_erase()
  99. {
  100.     if (!tloaded) tload();
  101.     fputs (cl, stdout);
  102. }
  103.  
  104. /* erase to end of line */
  105. c_eol()
  106. {
  107.     if (!tloaded) tload();
  108.     fputs (ce, stdout);
  109. }
  110.  
  111. #ifdef USE_NDELAY
  112. static char sav_char;    /* one character read-ahead for chk_char() */
  113. #endif
  114.  
  115. /* return 0 if there is a char that may be read without blocking, else -1 */
  116. chk_char()
  117. {
  118. #ifdef USE_NDELAY
  119.     if (!ttysetup) setuptty();
  120.     if (sav_char)
  121.         return (0);
  122.     fcntl (0, F_SETFL, O_NDELAY);    /* non-blocking read. FNDELAY on BSD */
  123.     if (read (0, &sav_char, 1) != 1)
  124.         sav_char = 0;
  125.     return (sav_char ? 0 : -1);
  126. #else
  127.     long n;
  128.     if (!ttysetup) setuptty();
  129.     ioctl (0, FIONREAD, &n);
  130.     return (n > 0 ? 0 : -1);
  131. #endif
  132. }
  133.  
  134. /* read the next char, blocking if necessary, and return it. don't echo.
  135.  * map the arrow keys if we can too into hjkl
  136.  */
  137. read_char()
  138. {
  139.     char c;
  140.     if (!ttysetup) setuptty();
  141.     fflush (stdout);
  142. #ifdef USE_NDELAY
  143.     fcntl (0, F_SETFL, 0);    /* blocking read */
  144.     if (sav_char) {
  145.         c = sav_char;
  146.         sav_char = 0;
  147.     } else
  148. #endif
  149.         read (0, &c, 1);
  150.     c = chk_arrow (c & 0177); /* just ASCII, please */
  151.     return (c);
  152. }
  153.  
  154. /* used to time out of a read */
  155. static got_alrm;
  156. static
  157. on_alrm()
  158. {
  159.     got_alrm = 1;
  160. }
  161.  
  162. /* see if c is the first of any of the curses arrow key sequences.
  163.  * if it is, read the rest of the sequence, and return the hjkl code
  164.  * that corresponds.
  165.  * if no match, just return c.
  166.  */
  167. static 
  168. chk_arrow (c)
  169. register char c;
  170. {
  171.     register char *seq;
  172.  
  173.     if (c == *(seq = kl) || c == *(seq = kd) || c == *(seq = ku)
  174.                          || c == *(seq = kr)) {
  175.         char seqa[32]; /* maximum arrow escape sequence ever expected */
  176.         unsigned l = strlen(seq);
  177.         seqa[0] = c;
  178.         if (l > 1) {
  179.         extern unsigned alarm();
  180.         /* cautiously read rest of arrow sequence */
  181.         got_alrm = 0;
  182.         (void) signal (SIGALRM, on_alrm);
  183.         alarm(2);
  184.         read (0, seqa+1, l-1);
  185.         alarm(0);
  186.         if (got_alrm)
  187.             return (c);
  188.         }
  189.         seqa[l] = '\0';
  190.         if (strcmp (seqa, kl) == 0)
  191.         return ('h');
  192.         if (strcmp (seqa, kd) == 0)
  193.         return ('j');
  194.         if (strcmp (seqa, ku) == 0)
  195.         return ('k');
  196.         if (strcmp (seqa, kr) == 0)
  197.         return ('l');
  198.     }
  199.     return (c);
  200. }
  201.  
  202. /* do whatever might be necessary to get the screen and/or tty back into shape.
  203.  */
  204. byetty()
  205. {
  206. #ifdef USE_TERMIO
  207.     ioctl (0, TCSETA, &orig_tio);
  208. #else
  209.     ioctl (0, TIOCSETP, &orig_sgtty);
  210. #endif
  211. #ifdef USE_NDELAY
  212.     fcntl (0, F_SETFL, 0);    /* be sure to go back to blocking read */
  213. #endif
  214.     ttysetup = 0;
  215. }
  216.  
  217. static 
  218. tload()
  219. {
  220.     extern char *getenv(), *tgetstr();
  221.     extern char *UP, *BC;
  222.     char *egetstr();
  223.     static char tbuf[512];
  224.     char rawtbuf[1024];
  225.     char *tp;
  226.     char *ptr;
  227.  
  228.     if (!(tp = getenv ("TERM"))) {
  229.         printf ("no TERM\n");
  230.         exit(1);
  231.     }
  232.  
  233.     if (!ttysetup) setuptty();
  234.     if (tgetent (rawtbuf, tp) != 1) {
  235.         printf ("Can't find termcap for %s\n", tp);
  236.         exit (1);
  237.     }
  238.     ptr = tbuf;
  239.     ku = egetstr ("ku", &ptr);
  240.     kd = egetstr ("kd", &ptr);
  241.     kl = egetstr ("kl", &ptr);
  242.     kr = egetstr ("kr", &ptr);
  243.     cm = egetstr ("cm", &ptr);
  244.     ce = egetstr ("ce", &ptr);
  245.     cl = egetstr ("cl", &ptr);
  246.     UP = egetstr ("up", &ptr);
  247.     if (!tgetflag ("bs"))
  248.         BC = egetstr ("bc", &ptr);
  249.     tloaded = 1;
  250. }
  251.  
  252. /* like tgetstr() but discard curses delay codes, for now anyways */
  253. static char *
  254. egetstr (name, sptr)
  255. char *name;
  256. char **sptr;
  257. {
  258.     extern char *tgetstr();
  259.     register char c, *s;
  260.  
  261.     s = tgetstr (name, sptr);
  262.     while (((c = *s) >= '0' && c <= '9') || c == '*')
  263.         s += 1;
  264.     return (s);
  265. }
  266.  
  267. /* set up tty for char-by-char read, non-blocking  */
  268. static
  269. setuptty()
  270. {
  271. #ifdef USE_TERMIO
  272.     struct termio tio;
  273.  
  274.     ioctl (0, TCGETA, &orig_tio);
  275.     tio = orig_tio;
  276.     tio.c_iflag &= ~ICRNL;    /* leave CR unchanged */
  277.     tio.c_oflag &= ~OPOST;    /* no output processing */
  278.     tio.c_lflag &= ~(ICANON|ECHO); /* no input processing, no echo */
  279.     tio.c_cc[VMIN] = 1;    /* return after each char */
  280.     tio.c_cc[VTIME] = 0;    /* no read timeout */
  281.     ioctl (0, TCSETA, &tio);
  282. #else
  283.     struct sgttyb sg;
  284.  
  285.     ioctl (0, TIOCGETP, &orig_sgtty);
  286.     sg = orig_sgtty;
  287.     sg.sg_flags &= ~ECHO;    /* do our own echoing */
  288.     sg.sg_flags &= ~CRMOD;    /* leave CR and LF unchanged */
  289.     sg.sg_flags |= XTABS;    /* no tabs with termcap */
  290.     sg.sg_flags |= CBREAK;    /* wake up on each char but can still kill */
  291.     ioctl (0, TIOCSETP, &sg);
  292. #endif
  293.     ttysetup = 1;
  294. }
  295. /* end of #ifdef UNIX */
  296. #endif
  297.  
  298. #ifdef TURBO_C
  299. #ifdef USE_ANSISYS
  300. #define    ESC    '\033'
  301. /* position cursor.
  302.  * (ANSI: ESC [ r ; c f) (r/c are numbers given in ASCII digits)
  303.  */
  304. c_pos (r, c)
  305. int r, c;
  306. {
  307.     printf ("%c[%d;%df", ESC, r, c);
  308. }
  309.  
  310. /* erase entire screen. (ANSI: ESC [ 2 J) */
  311. c_erase()
  312. {
  313. #ifdef AMIGA
  314.     char *tzname;
  315.  
  316.     if(!ttysetup)
  317.         {
  318.         if(!freopen("RAW:0/0/670/230/Ephem", "a+", stdout))
  319.             {
  320.             printf("Can't open window\n");
  321.             exit(10);
  322.             }
  323. #ifdef __SASC
  324.         winfh = _ufbs[fileno(stdout)].ufbfh;
  325.  
  326.         if((tzname = getenv("TZ")) == NULL)
  327.             tzname = "UTC-00";
  328.  
  329.         _TZ = tzname;
  330.         tzset();
  331. #else
  332.     ERROR!
  333. #endif
  334.         ttysetup = 1;
  335.         }
  336. #endif
  337.  
  338.     printf ("%c[2J", ESC);
  339. }
  340.  
  341. /* erase to end of line. (ANSI: ESC [ K) */
  342. c_eol()
  343. {
  344.     printf ("%c[K", ESC);
  345. }
  346. #else
  347. #include <dos.h>   
  348. union REGS rg;
  349.  
  350. /* position cursor.
  351.  */
  352. c_pos (r, c)
  353. int r, c;
  354. {
  355.         rg.h.ah = 2;
  356.         rg.h.bh = 0;
  357.         rg.h.dh = r-1;
  358.         rg.h.dl = c-1;
  359.         int86(16,&rg,&rg);
  360. }
  361.  
  362. /* erase entire screen.  */
  363. c_erase()
  364. {
  365.         int cur_cursor, i;
  366.         rg.h.ah = 3;
  367.         rg.h.bh = 0;
  368.         int86(16,&rg,&rg);
  369.         cur_cursor = rg.x.dx;
  370.         for(i = 0; i < 25; i++){
  371.             c_pos(i+1,1);
  372.             rg.h.ah = 10;
  373.             rg.h.bh = 0;
  374.             rg.h.al = 32;
  375.             rg.x.cx = 80;
  376.             int86(16,&rg,&rg);
  377.         }
  378.         rg.h.ah = 2;
  379.         rg.h.bh = 0;
  380.         rg.x.dx = cur_cursor;
  381.         int86(16,&rg,&rg);
  382.         
  383. }
  384.  
  385. /* erase to end of line.*/
  386. c_eol()
  387. {
  388.         int cur_cursor, i;
  389.         rg.h.ah = 3;
  390.         rg.h.bh = 0;
  391.         int86(16,&rg,&rg);
  392.         cur_cursor = rg.x.dx;
  393.         rg.h.ah = 10;
  394.         rg.h.bh = 0;
  395.         rg.h.al = 32;
  396.         rg.x.cx = 80 - rg.h.dl;
  397.         int86(16,&rg,&rg);
  398.         rg.h.ah = 2;
  399.         rg.h.bh = 0;
  400.         rg.x.dx = cur_cursor;
  401.         int86(16,&rg,&rg);
  402.  
  403. }
  404. #endif
  405.  
  406. #ifdef AMIGA
  407. #undef TURBO_C    /* kludge */
  408.  
  409. sleep(n)
  410. int n;
  411. {
  412.   if(n) Delay(n*50);
  413. }
  414.  
  415. chk_char()
  416. {
  417.   return(-(!WaitForChar(winfh, 10000)));
  418. }
  419.  
  420. read_ch1()
  421. {
  422.   unsigned char c;
  423.  
  424.   fflush(stdout);
  425.   if(Read(winfh, &c, 1) != 1) return -1;
  426.   return c;
  427. }
  428.  
  429. read_char()
  430. {
  431.   int c;
  432.  
  433.   c = read_ch1();
  434.   if(c == 0x9b)
  435.      {
  436.     c = read_ch1();
  437.     switch(c)
  438.        {
  439.          case 'A':
  440.         return 'k';    /* up */
  441.  
  442.          case 'B':
  443.         return 'j';    /* down */
  444.  
  445.          case 'C':
  446.         return 'l';    /* right */
  447.  
  448.          case 'D':
  449.         return 'h';    /* left */
  450.  
  451.          default:
  452.             while(c <= '9') c = read_ch1();
  453.         return c;
  454.        }
  455.      }
  456.   return c;
  457. }
  458.  
  459. byetty()
  460. {
  461. }
  462.  
  463. #else
  464.  
  465. /* return 0 if there is a char that may be read without blocking, else -1 */
  466. chk_char()
  467. {
  468.     return (kbhit() == 0 ? -1 : 0);
  469. }
  470.  
  471. /* read the next char, blocking if necessary, and return it. don't echo.
  472.  * map the arrow keys if we can too into hjkl
  473.  */
  474. read_char()
  475. {
  476.     int c;
  477.     fflush (stdout);
  478.     c = getch();
  479.     if (c == 0) {
  480.         /* get scan code; convert to direction hjkl if possible */
  481.         c = getch();
  482.         switch (c) {
  483.         case 0x4b: c = 'h'; break;
  484.         case 0x50: c = 'j'; break;
  485.         case 0x48: c = 'k'; break;
  486.         case 0x4d: c = 'l'; break;
  487.         }
  488.     }
  489.     return (c);
  490. }
  491.  
  492. /* do whatever might be necessary to get the screen and/or tty back into shape.
  493.  */
  494. byetty()
  495. {
  496. }
  497. #endif
  498.  
  499. /* end of #ifdef TURBO_C */
  500. #endif
  501.  
  502. #ifdef VMS
  503. #include <string.h>
  504. #include <iodef.h>
  505. #include <descrip.h>
  506. #include <dvidef.h>
  507. #include <smgtrmptr.h>
  508. #include <starlet.h>
  509. #include <lib$routines.h>
  510. #include <smg$routines.h>
  511.  
  512. /* Structured types for use in system calls */
  513. typedef struct{
  514.     unsigned short status;
  515.     unsigned short count;
  516.     unsigned int info;
  517. } io_status_block;
  518. typedef struct{
  519.     unsigned short buffer_length;
  520.     unsigned short item_code;
  521.     void *buffer_address;
  522.     unsigned short *return_length_address;
  523.     unsigned long terminator;
  524. } item_list;
  525.  
  526. static unsigned short ttchan = 0; /* channel number for terminal    */
  527. volatile static io_status_block iosb; /* I/O status block for operation */
  528.                                       /* currently in progress          */
  529. volatile static unsigned char input_buf; /* buffer to recieve input charac-*/
  530.                                          /* ter when operation completes   */
  531. static void *term_entry;          /* pointer to TERMTABLE entry     */
  532. #define MAXCAP 10
  533. static char ce[MAXCAP];           /* ce and cl capability strings for  */
  534. static char cl[MAXCAP];           /* this terminal type                */
  535.  
  536. /* Declaration of special keys to be recoqnized on input */
  537. /* Number of special keys defined */
  538. #define MAXKEY 4
  539. /* TERMTABLE capability codes for the keys */
  540. static long capcode[MAXKEY] = {SMG$K_KEY_UP_ARROW,SMG$K_KEY_DOWN_ARROW,
  541.     SMG$K_KEY_RIGHT_ARROW,SMG$K_KEY_LEFT_ARROW};
  542. /* character codes to be returned by read_char when a special key is presssed */
  543. static int retcode[MAXKEY] = {'k','j','l','h'};
  544. /* the actual capability strings from the key */
  545. static char keycap[MAXKEY][MAXCAP];
  546.  
  547. static char special_buffer[MAXCAP];   /* buffer for reading special key */
  548. static int chars_in_buffer;           /* number of characters in buffer */
  549.  
  550. /* set up the structures for this I/O module */
  551. inittt()
  552. {
  553.     unsigned int status;   /* system routine return status */
  554.     $DESCRIPTOR(tt,"TT");  /* terminal name */
  555.     item_list itmlst;      /* item list for $getdvi obtaining term type */
  556.     unsigned long devtype; /* terminal type returned form $getdvi */
  557.     unsigned short retlen; /* return length from $getdvi */
  558.     unsigned long lenret;  /* return length from smg$get_term_data */
  559.     unsigned long maxlen;  /* maximum return length */
  560.     unsigned long cap_code;/* capability code */
  561. #define MAXINIT 20
  562.     char init_string[MAXINIT];/* string to initialize terminal */
  563.     int key;
  564.  
  565.     /* Assign a channel to the terminal */
  566.     if (!((status = sys$assign(&tt,&ttchan,0,0))&1)) lib$signal(status);
  567.  
  568.     /* Get terminal type. Note that it is possible to use the same
  569.      * iosb at this stage, because no I/O is initiated yet.
  570.      */
  571.     itmlst.buffer_length = 4;
  572.     itmlst.item_code = DVI$_DEVTYPE;
  573.     itmlst.buffer_address = &devtype;
  574.     itmlst.return_length_address = &retlen;
  575.     itmlst.terminator = 0;
  576.     if (!((status = sys$getdviw(0,ttchan,0,&itmlst,&iosb,0,0,0))&1))
  577.         lib$signal(status);
  578.     if (!(iosb.status&1)) lib$signal(iosb.status);
  579.  
  580.     /* Get the TERMTABLE entry corresponding to the terminal type */
  581.     if (!((status = smg$init_term_table_by_type(&devtype,
  582.         &term_entry))&1)) lib$signal(status);
  583.  
  584.     /* Get the initialisation string and initialize terminal */
  585.     cap_code = SMG$K_INIT_STRING;
  586.     maxlen = MAXINIT - 1;
  587.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  588.         &lenret,init_string))&1)) lib$signal(status);
  589.     init_string[lenret] = '\0';
  590.     fputs(init_string,stdout);
  591.     fflush(stdout);
  592.  
  593.     /* Get ce and cl capabilities, these are static */
  594.     cap_code = SMG$K_ERASE_TO_END_LINE;
  595.     maxlen = MAXCAP-1;
  596.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  597.         &lenret,ce))&1)) lib$signal(status);
  598.     ce[lenret] = '\0';
  599.  
  600.     cap_code = SMG$K_ERASE_WHOLE_DISPLAY;
  601.     maxlen = MAXCAP-1;
  602.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  603.         &lenret,cl))&1)) lib$signal(status);
  604.     cl[lenret] = '\0';
  605.  
  606.     /* Here one could obtain line drawing sequences, please feel free
  607.        to implement it ... */
  608.  
  609.     /* Get special keys to be recoqnized on input */
  610.     for (key = 0;key<MAXKEY;key++)
  611.     {
  612.         maxlen = MAXCAP-1;
  613.         if (!((status = smg$get_term_data(&term_entry,&capcode[key],
  614.             &maxlen,&lenret,keycap[key]))&1)) lib$signal(status);
  615.         keycap[key][lenret] = '\0';
  616.     }
  617.  
  618.     /* Initiate first input operation, NOECHO.
  619.      * NOFILTR allows any character to get through, this makes it
  620.      * possible to implement arrow recoqnition, and also makes
  621.      * DEL and BS get through.
  622.      * We don't wait for the operation to complete.
  623.      * Note that stdout has already been fflush'ed above.
  624.      */
  625.     if (!((status = sys$qio(0,ttchan,
  626.         IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  627.         &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  628.  
  629.     /* Initialise special key buffer */
  630.     chars_in_buffer = 0;
  631. } /* inittt */
  632.  
  633.  
  634. /* return 0 if there is a char that may be read without blocking, else -1 */
  635. chk_char()
  636. {
  637.     if (!ttchan) inittt();
  638.  
  639.         return ( chars_in_buffer != 0 ? 0 :(iosb.status == 0 ? -1 : 0));
  640. }
  641.  
  642. /* read the next char, blocking if necessary, and return it. don't echo.
  643.  * map the arrow keys if we can too into hjkl
  644.  */
  645. read_char()
  646. {
  647.     unsigned int status;
  648.     int buf;
  649.     int i;
  650.     int found_key;
  651.     int key;
  652.     int this_len;
  653.     int match;
  654.  
  655.     if (!ttchan) inittt();
  656.  
  657.     /* If we attempted to read an special key previously, there are characters
  658.      * left in the buffer, return these before doing more I/O
  659.      */
  660.     if (chars_in_buffer!=0){
  661.         buf = special_buffer[0];
  662.         chars_in_buffer--;
  663.         for (i = 0;i<chars_in_buffer;i++)
  664.         {
  665.             special_buffer[i] = special_buffer[i+1];
  666.         }
  667.         special_buffer[chars_in_buffer] = '\0';
  668.     }
  669.     else {
  670.  
  671.         /* Loop over characters read, the loop is terminated when the
  672.          * characters read so far do not match any of the special keys
  673.          * or when the characters read so far is identical to one of
  674.          * the special keys.
  675.          */
  676.  
  677.         do
  678.         {
  679.             /* Wait for I/O to complete */
  680.             if (!((status = sys$synch(0,&iosb))&1)) lib$signal(status);
  681.             special_buffer[chars_in_buffer] = input_buf;
  682.             chars_in_buffer++;
  683.             special_buffer[chars_in_buffer] = '\0';
  684.  
  685.             /* Initiate next input operation */
  686.             fflush (stdout);
  687.             if (!((status = sys$qio(0,ttchan,
  688.                 IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  689.                 &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  690.  
  691.  
  692.             /* Check for match with all special strings */
  693.             match = 0;
  694.             found_key = MAXKEY;
  695.             for (key = 0;key<MAXKEY;key++)
  696.             {
  697.                 this_len = strlen(keycap[key]);
  698.                 if (this_len<chars_in_buffer) continue;
  699.                 if (!strncmp(keycap[key],special_buffer,chars_in_buffer)){
  700.                     match = -1;
  701.                     if (this_len == chars_in_buffer){
  702.                         found_key = key;
  703.                         break;
  704.                     }
  705.                 }
  706.             }
  707.         }
  708.         while (match && (found_key == MAXKEY));
  709.  
  710.         /* If one of the keys matches the input string, return the
  711.          * corresponding  key code
  712.          */
  713.         if (found_key != MAXKEY)
  714.         {
  715.             buf = retcode[found_key];
  716.             chars_in_buffer = 0;
  717.         }
  718.         else /* return first character and store the rest in the buffer */
  719.         {
  720.             buf = special_buffer[0];
  721.             chars_in_buffer--;
  722.             for (i = 0;i<chars_in_buffer;i++)
  723.             {
  724.                 special_buffer[i] = special_buffer[i+1];
  725.             }
  726.         }
  727.         special_buffer[chars_in_buffer] = '\0';
  728.     }
  729.     return(buf);
  730. }
  731.  
  732. /* do whatever might be necessary to get the screen and/or tty back into shape.
  733.  */
  734. byetty()
  735. {
  736.     unsigned int status;
  737.  
  738.     if (ttchan)
  739.     {
  740.         /* There is no string in SMG to send to the terminal when
  741.          * terminating, one could clear the screen, move the cursor to
  742.          * the last line, or whatever. This program clears the screen
  743.          * anyway before calling this routine, so we do nothing.
  744.          */
  745.  
  746.  
  747.  
  748.         /* The following is not really neccessary, it will be done at program
  749.          * termination anyway, but if someone tries to use the I/O routines agai
  750.    n
  751.          * it might prove useful...
  752.          */
  753.         if (!((status = smg$del_term_table())&1)) lib$signal(status);
  754.         if (!((status = sys$dassgn(ttchan))&1)) lib$signal(status);
  755.         /* This also cancels any outstanding I/O on the channel */
  756.         ttchan = 0; /* marks terminal I/O as not initialized */
  757.     }
  758. }
  759.  
  760. /* position cursor. */
  761. c_pos (r, c)
  762. int r, c;
  763. {
  764.     unsigned long vector[3]; /* argument vector (position)   */
  765.     unsigned long status;    /* system service return status */
  766.     long lenret;             /* length of returned string    */
  767.     long maxlen;             /* maximum return length        */
  768.     unsigned long capcode;   /* capability code              */
  769.     char seq[2*MAXCAP];      /* returned string              */
  770.  
  771.     if (!ttchan) inittt();
  772.  
  773.     /* Set cursor depends on the position, therefore we have to call
  774.      * get_term_data for each operation
  775.      */
  776.     vector[0] = 2;
  777.     vector[1] = r;
  778.     vector[2] = c;
  779.     capcode = SMG$K_SET_CURSOR_ABS;
  780.     maxlen = 2*MAXCAP-1;
  781.     if (!((status = smg$get_term_data(&term_entry,&capcode,&maxlen,
  782.         &lenret,seq,vector))&1)) lib$signal(status);
  783.     seq[lenret] = '\0';
  784.  
  785.     fputs(seq,stdout);
  786. }
  787.  
  788. /* erase entire screen. */
  789. c_erase()
  790. {
  791.     if (!ttchan) inittt();
  792.  
  793.     fputs(cl,stdout);
  794. }
  795.  
  796. /* erase to end of line. */
  797. c_eol()
  798. {
  799.     if (!ttchan) inittt();
  800.  
  801.     fputs(ce,stdout);
  802. }
  803. /* end of #ifdef VMS */
  804. #endif
  805.  
  806. /* read up to max chars into buf, with cannonization.
  807.  * add trailing '\0' (buf is really max+1 chars long).
  808.  * return count of chars read (not counting '\0').
  809.  * assume cursor is already positioned as desired.
  810.  * if type END when n==0 then return -1.
  811.  */
  812. read_line (buf, max)
  813. char buf[];
  814. int max;
  815. {
  816.     static char erase[] = "\b \b";
  817.     int n, c;
  818.     int done;
  819.  
  820. #ifdef UNIX
  821.     if (!ttysetup) setuptty();
  822. #endif
  823.  
  824.     for (done = 0, n = 0; !done; )
  825.         switch (c = read_char()) {    /* does not echo */
  826.         case cntrl('h'):    /* backspace or */
  827.         case 0177:        /* delete are each char erase */
  828.         if (n > 0) {
  829.             fputs (erase, stdout);
  830.             n -= 1;
  831.         }
  832.         break;
  833.         case cntrl('u'):        /* line erase */
  834.         while (n > 0) {
  835.             fputs (erase, stdout);
  836.             n -= 1;
  837.         }
  838.         break;
  839.         case '\r':    /* EOL */
  840.         done++;
  841.         break;
  842.         default:            /* echo and store, if ok */
  843.         if (n == 0 && c == END)
  844.             return (-1);
  845.         if (n >= max)
  846.             putchar (cntrl('g'));
  847.         else if (c >= ' ') {
  848.             putchar (c);
  849.             buf[n++] = c;
  850.         }
  851.         }
  852.  
  853.     buf[n] = '\0';
  854.     return (n);
  855. }
  856.